package com.common.shiro.realm;

import com.common.constants.ShiroConstant;
import com.common.model.entity.SysUser;
import com.core.mapper.SysPermissionMapper;
import com.core.mapper.SysRoleMapper;
import com.core.mapper.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Set;

@Slf4j
public class CustomRealm extends AuthorizingRealm {

  // 注入二级缓存管理器（本地Caffeine + 远程Redis）
  @Resource(name = "twoLevelCacheManager")
  private CacheManager twoLevelCacheManager;

  @Resource
  private SysUserMapper sysUserMapper;

  @Resource
  private SysRoleMapper sysRoleMapper;

  @Resource
  private SysPermissionMapper sysPermissionMapper;

  // ======================== 认证逻辑（Authentication）========================
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
          throws AuthenticationException {

    String userName = (String) authenticationToken.getPrincipal();
    String userPassword = new String((char[]) authenticationToken.getCredentials());

    log.debug("CustomRealm 认证开始：userName={}", userName);

    // 1. 获取用户缓存实例：使用【缓存名称】CACHE_NAME_SYS_USER（无后缀:）
    Cache userCache = twoLevelCacheManager.getCache(ShiroConstant.CACHE_NAME_SYS_USER);
    // 2. 业务缓存键：直接用userName（不再拼接前缀，前缀由缓存名称+分隔符承担）
    String userCacheKey = userName;

    // 3. 从二级缓存获取用户信息
    Cache.ValueWrapper userCacheWrapper = userCache.get(userCacheKey);
    SysUser sysUser = null;
    if (userCacheWrapper != null) {
      sysUser = (SysUser) userCacheWrapper.get();
      log.info("CustomRealm 认证：从二级缓存获取用户信息，userName={}", userName);
    }

    // 4. 二级缓存未命中，从数据库查询
    if (sysUser == null) {
      sysUser = sysUserMapper.getUserByUserName(userName);
      log.info("CustomRealm 认证：二级缓存未命中，从数据库获取用户信息，userName={}, sysUser={}", userName, sysUser);

      if (sysUser == null) {
        throw new UnknownAccountException("该账号不存在");
      }

      // 5. 写入二级缓存：键为userName，值为sysUser
      userCache.put(userCacheKey, sysUser);
      log.info("CustomRealm 认证：用户信息写入二级缓存，cacheName={}, cacheKey={}",
              ShiroConstant.CACHE_NAME_SYS_USER, userCacheKey);
    }

    // 6. 密码校验
    if (!sysUser.getUserPassword().equals(userPassword)) {
      throw new UnknownAccountException("密码不正确");
    }

    return new SimpleAuthenticationInfo(sysUser, userPassword, getName());
  }

  // ======================== 授权逻辑（Authorization）========================
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();
    String username = sysUser.getUserName();
    log.debug("CustomRealm 授权开始：userName={}", username);

    // 1. 获取角色缓存实例：使用【缓存名称】CACHE_NAME_SYS_ROLE
    Cache roleCache = twoLevelCacheManager.getCache(ShiroConstant.CACHE_NAME_SYS_ROLE);
    String roleCacheKey = username; // 业务键直接用userName

    // 2. 处理角色缓存
    Cache.ValueWrapper roleCacheWrapper = roleCache.get(roleCacheKey);
    Set<String> userRoleSet = null;
    if (roleCacheWrapper != null) {
      userRoleSet = (Set<String>) roleCacheWrapper.get();
      log.info("CustomRealm 授权：从二级缓存获取角色信息，userName={}, userRoleSet={}", username, userRoleSet);
    }
    if (CollectionUtils.isEmpty(userRoleSet)) {
      userRoleSet = sysUserMapper.getUserRoleByUserName(username);
      log.info("CustomRealm 授权：角色缓存未命中，从数据库获取角色信息，userName={}, userRoleSet={}", username, userRoleSet);
      roleCache.put(roleCacheKey, userRoleSet); // 写入缓存
      log.info("CustomRealm 授权：角色信息写入二级缓存，cacheName={}, cacheKey={}",
              ShiroConstant.CACHE_NAME_SYS_ROLE, roleCacheKey);
    }

    // 3. 获取权限缓存实例：使用【缓存名称】CACHE_NAME_SYS_PERMISSION
    Cache permissionCache = twoLevelCacheManager.getCache(ShiroConstant.CACHE_NAME_SYS_PERMISSION);
    String permissionCacheKey = username; // 业务键直接用userName

    // 4. 处理权限缓存
    Cache.ValueWrapper permissionCacheWrapper = permissionCache.get(permissionCacheKey);
    Set<String> userPermissionSet = null;
    if (permissionCacheWrapper != null) {
      userPermissionSet = (Set<String>) permissionCacheWrapper.get();
      log.info("CustomRealm 授权：从二级缓存获取权限信息，userName={}, userPermissionSet={}", username, userPermissionSet);
    }
    if (CollectionUtils.isEmpty(userPermissionSet)) {
      userPermissionSet = sysUserMapper.getPermissionsByUsername(username);
      log.info("CustomRealm 授权：权限缓存未命中，从数据库获取权限信息，userName={}, userPermissionSet={}", username, userPermissionSet);
      permissionCache.put(permissionCacheKey, userPermissionSet); // 写入缓存
      log.info("CustomRealm 授权：权限信息写入二级缓存，cacheName={}, cacheKey={}",
              ShiroConstant.CACHE_NAME_SYS_PERMISSION, permissionCacheKey);
    }

    // 5. 组装授权信息
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    info.addRoles(userRoleSet);
    info.addStringPermissions(userPermissionSet);
    return info;
  }

  // ======================== 缓存清理方法 ========================
  public void clearUserCache(String userName) {
    // 1. 获取各缓存实例（使用新的缓存名称）
    Cache userCache = twoLevelCacheManager.getCache(ShiroConstant.CACHE_NAME_SYS_USER);
    Cache roleCache = twoLevelCacheManager.getCache(ShiroConstant.CACHE_NAME_SYS_ROLE);
    Cache permissionCache = twoLevelCacheManager.getCache(ShiroConstant.CACHE_NAME_SYS_PERMISSION);

    // 2. 业务缓存键直接用userName
    String userCacheKey = userName;
    String roleCacheKey = userName;
    String permissionCacheKey = userName;

    // 3. 清除缓存
    userCache.evict(userCacheKey);
    roleCache.evict(roleCacheKey);
    permissionCache.evict(permissionCacheKey);

    log.info("CustomRealm 缓存清理：清除用户[{}]的所有缓存（用户/角色/权限）", userName);
  }
}
